Skip to main content

TRCustomRemotes

Sometimes you need to send an event or call a function alongside replicated data — a "you were hit" signal, a "buy this item" request, and so on. Rather than managing your own RemoteEvents, TableReplicator lets you attach custom remotes directly to a replicator. They're addressed per-replicator and (optionally) ordered with that replicator's data ops.

Two proxies, one convention

The server talks to clients through replicator.Client; the client talks to the server through replicator.Server. So the side you're sending to is the proxy you use. A remote registered as "Hit" is fired on the server via replicator.Client.Hit and listened to on the client via replicator.Server.Hit.


Declaring remotes

The quickest way is the Client field on ServerReplicator.new. Use the sentinel helpers for signals, and a plain function for a callable:

local replicator = ServerReplicator.new({
	Namespace = "Combat",
	Targets = player,
	Client = {
		Hit = ServerReplicator.createRemoteEvent(),          -- a signal
		Whiff = ServerReplicator.createUnreliableEvent(),    -- an unreliable signal
		GetLoadout = function(self, player, slot)            -- a function
			return loadouts[player][slot]
		end,
	},
})
Client declarations are ordered

Remotes declared in the Client table are registered as their ordered variants: a signal there behaves like RegisterOrderedRemoteSignal, and a function like RegisterOrderedRemoteFunction. That means their delivery is interleaved with data ops in frame order (see TR Performance & Ordering), and clients must call InvokeAsync — not Invoke — on such functions. If you want the unordered variants, register them explicitly instead (next section). createUnreliableEvent() is always unreliable regardless.

Registering explicitly

Every remote can also be registered after construction. This is the only way to get the unordered reliable variants:

replicator:RegisterRemoteSignal("Hit")             -- reliable, unordered
replicator:RegisterRemoteUnreliableSignal("Whiff") -- unreliable
replicator:RegisterOrderedRemoteSignal("Combo")    -- reliable, ordered with data

replicator:RegisterRemoteFunction("GetScore", function(self, player) return 42 end)
replicator:RegisterOrderedRemoteFunction("Buy", function(self, player, itemId) ... end)

Here's the full picture — how a remote is declared, what it is, and how the client calls it:

Register with Kind Client uses
RegisterRemoteSignal reliable signal Connect / Fire
RegisterRemoteUnreliableSignal unreliable signal Connect / Fire
RegisterOrderedRemoteSignal (or a signal in Client) ordered signal Connect / Fire
RegisterRemoteFunction unordered function Invoke (yields)
RegisterOrderedRemoteFunction (or a function in Client) ordered function InvokeAsync (Promise)

Signals

Signals are two-way: the server can fire to clients, and clients can fire back.

Server → client. Fire through replicator.Client[name]:

replicator.Client.Hit:Fire(player, "headshot")   -- one active player
replicator.Client.Hit:FireAll("headshot")         -- all active players
replicator.Client.Hit:FireExcept(player, "...")   -- all except one (or a list)
replicator.Client.Hit:FirePredicate(function(plr)
	return plr.Team == redTeam
end, "...")                                        -- a filtered audience

Client → server. Listen and fire through replicator.Server[name]:

-- On the client
replicator.Server.Hit:Connect(function(hitType)
	print("Hit:", hitType)
end)
replicator.Server.Hit:Fire("blocked")        -- fire back to the server
replicator.Server.Hit:FireUnreliable("...")  -- force unreliable delivery

-- On the server, receive client fires:
replicator.Client.Hit:Connect(function(player, ...) end)
replicator.Client.Hit:Wait()                 -- yields until any client fires

Functions

A function is registered on the server and called from the client. Which client method to use depends on whether it's ordered:

-- Unordered function -> client Invoke (yields for the result)
replicator:RegisterRemoteFunction("GetScore", function(self, player)
	return scores[player]
end)
local score = replicator.Server.GetScore:Invoke()

-- Ordered function -> client InvokeAsync (returns a Promise; response arrives
-- after any data ops the server queued in the same frame)
replicator:RegisterOrderedRemoteFunction("Buy", function(self, player, itemId)
	return purchase(player, itemId)
end)
replicator.Server.Buy:InvokeAsync("sword"):andThen(function(ok) end)

The server-side handler always receives (self, player, ...)self is the replicator and player is the caller.

Wrong-method warnings

Calling Invoke on a signal, Connect on a function, or Invoke on an ordered function all emit a warning naming the correct method — a handy signal you've mixed up a remote's kind.


See also

Show raw api
{
    "functions": [],
    "properties": [],
    "types": [],
    "name": "TR Custom Remotes",
    "desc": "Sometimes you need to send an *event* or call a *function* alongside replicated\ndata — a \"you were hit\" signal, a \"buy this item\" request, and so on. Rather than\nmanaging your own `RemoteEvent`s, TableReplicator lets you attach custom remotes\ndirectly to a replicator. They're addressed per-replicator and (optionally) ordered\nwith that replicator's data ops.\n\n:::note Two proxies, one convention\nThe server talks to clients through `replicator.Client`; the client talks to the\nserver through `replicator.Server`. So the side you're **sending to** is the proxy\nyou use. A remote registered as `\"Hit\"` is fired on the server via\n`replicator.Client.Hit` and listened to on the client via `replicator.Server.Hit`.\n:::\n\n---\n## Declaring remotes\n\nThe quickest way is the `Client` field on `ServerReplicator.new`. Use the sentinel\nhelpers for signals, and a plain function for a callable:\n\n```lua\nlocal replicator = ServerReplicator.new({\n\tNamespace = \"Combat\",\n\tTargets = player,\n\tClient = {\n\t\tHit = ServerReplicator.createRemoteEvent(),          -- a signal\n\t\tWhiff = ServerReplicator.createUnreliableEvent(),    -- an unreliable signal\n\t\tGetLoadout = function(self, player, slot)            -- a function\n\t\t\treturn loadouts[player][slot]\n\t\tend,\n\t},\n})\n```\n\n:::caution **Client** declarations are ordered\nRemotes declared in the `Client` table are registered as their **ordered** variants:\na signal there behaves like `RegisterOrderedRemoteSignal`, and a function like\n`RegisterOrderedRemoteFunction`. That means their delivery is interleaved with data\nops in frame order (see [TR Performance & Ordering](/api/TR%20Performance%20&%20Ordering)),\nand clients must call `InvokeAsync` — not `Invoke` — on such functions.\nIf you want the **unordered** variants, register them explicitly instead (next\nsection). `createUnreliableEvent()` is always unreliable regardless.\n:::\n\n### Registering explicitly\n\nEvery remote can also be registered after construction. This is the only way to get\nthe unordered reliable variants:\n\n```lua\nreplicator:RegisterRemoteSignal(\"Hit\")             -- reliable, unordered\nreplicator:RegisterRemoteUnreliableSignal(\"Whiff\") -- unreliable\nreplicator:RegisterOrderedRemoteSignal(\"Combo\")    -- reliable, ordered with data\n\nreplicator:RegisterRemoteFunction(\"GetScore\", function(self, player) return 42 end)\nreplicator:RegisterOrderedRemoteFunction(\"Buy\", function(self, player, itemId) ... end)\n```\n\nHere's the full picture — how a remote is declared, what it is, and how the client\ncalls it:\n\n| Register with | Kind | Client uses |\n| --- | --- | --- |\n| `RegisterRemoteSignal` | reliable signal | `Connect` / `Fire` |\n| `RegisterRemoteUnreliableSignal` | unreliable signal | `Connect` / `Fire` |\n| `RegisterOrderedRemoteSignal` *(or a signal in `Client`)* | ordered signal | `Connect` / `Fire` |\n| `RegisterRemoteFunction` | unordered function | `Invoke` (yields) |\n| `RegisterOrderedRemoteFunction` *(or a function in `Client`)* | ordered function | `InvokeAsync` (Promise) |\n\n---\n## Signals\n\nSignals are two-way: the server can fire to clients, and clients can fire back.\n\n**Server → client.** Fire through `replicator.Client[name]`:\n\n```lua\nreplicator.Client.Hit:Fire(player, \"headshot\")   -- one active player\nreplicator.Client.Hit:FireAll(\"headshot\")         -- all active players\nreplicator.Client.Hit:FireExcept(player, \"...\")   -- all except one (or a list)\nreplicator.Client.Hit:FirePredicate(function(plr)\n\treturn plr.Team == redTeam\nend, \"...\")                                        -- a filtered audience\n```\n\n**Client → server.** Listen and fire through `replicator.Server[name]`:\n\n```lua\n-- On the client\nreplicator.Server.Hit:Connect(function(hitType)\n\tprint(\"Hit:\", hitType)\nend)\nreplicator.Server.Hit:Fire(\"blocked\")        -- fire back to the server\nreplicator.Server.Hit:FireUnreliable(\"...\")  -- force unreliable delivery\n\n-- On the server, receive client fires:\nreplicator.Client.Hit:Connect(function(player, ...) end)\nreplicator.Client.Hit:Wait()                 -- yields until any client fires\n```\n\n\n## Functions\n\nA function is registered on the server and called from the client. Which client\nmethod to use depends on whether it's ordered:\n\n```lua\n-- Unordered function -> client Invoke (yields for the result)\nreplicator:RegisterRemoteFunction(\"GetScore\", function(self, player)\n\treturn scores[player]\nend)\nlocal score = replicator.Server.GetScore:Invoke()\n\n-- Ordered function -> client InvokeAsync (returns a Promise; response arrives\n-- after any data ops the server queued in the same frame)\nreplicator:RegisterOrderedRemoteFunction(\"Buy\", function(self, player, itemId)\n\treturn purchase(player, itemId)\nend)\nreplicator.Server.Buy:InvokeAsync(\"sword\"):andThen(function(ok) end)\n```\n\nThe server-side handler always receives `(self, player, ...)` — `self` is the\nreplicator and `player` is the caller.\n\n:::tip Wrong-method warnings\nCalling `Invoke` on a signal, `Connect` on a function, or `Invoke` on an *ordered*\nfunction all emit a warning naming the correct method — a handy signal you've mixed\nup a remote's kind.\n:::\n\n---\n### See also\n\n- **[TR Getting Started](/api/TR%20Getting%20Started)** — the `Client` config field in context.\n- **[TR Performance & Ordering](/api/TR%20Performance%20&%20Ordering)** — what \"ordered\" delivery guarantees and costs.",
    "source": {
        "line": 139,
        "path": "lib/tablereplicator/src/Docs/TR_Custom_Remotes.luau"
    }
}